<?php

/**
* This is the model class for table "email_message".
*
* The followings are the available columns in table 'email_message':
* @property integer $id
* @property string $name
* @property string $sender_email
* @property string $sender_name
* @property string $class_path
* @property string $subject
* @property string $template
* @property integer $status
* @property string $create_date
* @property string $update_date
*/
class EmailMessage extends CActiveRecord {

    /**
    * From whom email is sent. Could be different from the user used by SMTP to send email
    * Please note that some SMTP server like Gmail does not allow alternate sender information
    * and this setting won't take effect.
    * 
    * @var array
    */
    public $from = null;

    /**
    * Array of recipients. Each reciepient could be 
    * - a string of email address or
    * - an array(email => name)
    * 
    * @var mixed
    */
    public $recipients = array();
    protected $sentEmails = array();
    protected $parameters;

    /**
    * Derived EmailMessage class must implement this function
    * to return an array of parameter names used by the email template
    */
    public function getParameters() {

        Yii::import($this->class_path);
        eval('$model= new ' . end(explode('.', $this->class_path)) . ';');
        return $model->getParameters();
        //throw new CException(get_class($this).' must implement getParameters() function to return an array of parameters for the email.');
    }

    /**
    * Derived EmailMessage class must implement this function
    * to return an array of param => value used by the email template
    */
    public function getData() {
        $data = array();
        foreach ($this->Parameters as $key => $value) {
            $method = "get{$key}";
            $data[$key] = $this->$method();
        }
        return $data;
    }

    public function validateSubject() {
        /**
        * Todo: parse the subject and validate against the $params.
        * The subject if having param must be in form or {$param_name}.
        * The param_name must be one of param in $param array
        */
        $params = $this->Parameters;
        preg_match_all('/{\$data\[\"(.*?)\"]}/', $this->subject, $match);
        if (is_array($params)) {	   
            if (is_array($match[1]))		
                foreach ($match[1] as $value) {
                    if (!in_array($value, array_keys($params))) {
                        $this->addError('subject','The parameter "'.$value.'" is not in list');
                        return false;
                    }
            }
        }
        else{
            if (is_array($match[1])){
                $this->addError('subject', 'This email is not support parameter.');
                return false;
            }		
        }
        return true;


    }

    public function validateTemplate() {
        /**
        * Todo: parse the template text and validate against the $params.
        * The template can have less params than in the list but CANNOT 
        * have param which is not in the list.
        */
        $params = $this->Parameters;	
        preg_match_all('/<\?php echo (.*?);\?>/', $this->template, $match);	
        if (is_array($params)) {	   
            if (is_array($match[1]))		
                foreach ($match[1] as $value) {
                    $value=str_replace('$','',$value);
                    if (!in_array($value, array_keys($params))) {			                        
                        if(!preg_match('/(.*?)::(.*?)/',$value)){                            
                            $this->addError('template', 'The parameter "'.$value.'" is not in list');
                            return false;
                        } 
                        elseif(!hasParam($value,true)){
                            $this->addError('template', 'The parameter "'.$value.'" is undefined');
                            return false;
                        }
                    }
            }
        }
        else{
            if (is_array($match[1])){
                $this->addError('template', 'This email is not support parameter.');
                return false;
            }		
        }
        return true;
    }

    /**
    * Send email to recipients
    * @return int total email sent
    */
    public function send() {
        if (count($this->recipients) < 1)
            throw new Exception("Error while sending {$this->name} email. There is no recipient to send !");
        //find subject for email
        $modelClass = get_class($this);
        $modelEmail = self::model()->find("class_path LIKE '%." . $modelClass . "'");

        if (empty($modelEmail)) {
            throw new Exception("Cant not find email template");
        }

        $data = $this->getData();
        //        CVarDumper::dump($data);die;
        Yii::import('application.modules.Core.extensions.vendors.mail.YiiMailMessage');
        Yii::app()->mail->viewPath ='site.runtime.emails'; ;                    
        $message = new YiiMailMessage();
        $message->view = $this->getView($modelEmail);


        if (!empty($modelEmail->sender_email)){
            $this->from=array($modelEmail->sender_email=>$modelEmail->sender_name);            
            $message->setFrom($this->from);
        }            
        elseif (hasParam('Settings::ADMIN_EMAIL')){
            $message->setFrom(array(Settings::MAIL_SERDER_ADDRESS => (hasParam('Settings::MAIL_SENDER_NAME') ? Settings::MAIL_SENDER_NAME : Settings::SITE_NAME)));
        }        

        $message->headers->addTextHeader("Reply-to", Settings::MAIL_SERDER_ADDRESS);
        $message->headers->addTextHeader("X-Priority", "3");
        $message->headers->addTextHeader("X-Mailer", 'Swift Mailer ' . Swift::VERSION);

        foreach ($this->recipients as $to) {
            // if subject contains parameter, it should be in form: {$data['param_name']}
            // for example: {$data['username']}, you are now our gold member
            eval('$subject = "' . $modelEmail->subject . '";');
            $message->setSubject($subject);
            $message->setBody($data, 'text/html');

            $message->addTo($to);
            /**
            * todo: 1. need to improve to log error message
            * todo: 2. raise onBeforeSend event with $message as an event parameter
            */
            if (Yii::app()->mail->send($message))
                $this->sentEmails[] = $to;
            // todo: raise onAfterSend event with $message as an event parameter
        }
        return count($this->sentEmails);
    }

    public function getView($model) {
        $view = self::convertPath2FileName($model->class_path);

        $templateFolder = Yii::app()->runtimePath . '/emails';
        /**
        * Todo: make sure the view is existing in $templateFolder
        * if not, generate a new file.
        */
        return $view;
    }

    /**
    * Return file name of the class_path
    * 
    */
    public static function convertPath2FileName($class_path) {
        //remove 'models' in the path
        $class_path = str_replace('models', '', $class_path);
        //replace '..' to '.'
        $class_path = str_replace('..', '.', $class_path);
        //replace '.' to '_'
        $class_path = str_replace('.', '_', $class_path);
        return $class_path;
    }

    public static function getFriendlySubject($subject){
        $subject=str_replace('{$data["','<#',$subject);	
        return str_replace('"]}','#>',$subject);	
    }

    /**
    * Returns the static model of the specified AR class.
    * @return EmailMessage the static model class
    */
    public static function model($className=__CLASS__) {
        return parent::model($className);
    }

    /**
    * @return string the associated database table name
    */
    public function tableName() {
        return SITE_TABLE_PREFIX.'email_message';
    }

    /**
    * @return array validation rules for model attributes.
    */
    public function rules() {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
        array('name, sender_email, sender_name, subject, template', 'required'),
        array('subject', 'validateSubject'),
        array('template', 'validateTemplate'),
        array('status', 'numerical', 'integerOnly' => true),
        array('name,sender_email, sender_name, class_path', 'length', 'max' => 255),
        array('create_date, update_date', 'safe'),
        // The following rule is used by search().
        // Please remove those attributes that should not be searched.
        array('id, name, sender_enmail, sender_name, class_path, template, status, create_date, update_date', 'safe', 'on' => 'search'),
        );
    }

    /**
    * @return array relational rules.
    */
    public function relations() {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
        );
    }

    /**
    * @return array customized attribute labels (name=>label)
    */
    public function attributeLabels() {
        return array(
        'id' => 'ID',
        'name' => 'Name',
        'sender_email'=>'Sender Email',
        'sender_name'=>'Sender Name',
        'class_path' => 'Class Path',
        'subject' => 'Subject',
        'template' => 'Template',
        'status' => 'Status',
        'create_date' => 'Create Date',
        'update_date' => 'Update Date',  
        );
    }

    /**
    * Retrieves a list of models based on the current search/filter conditions.
    * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
    */
    public function search() {
        // Warning: Please modify the following code to remove attributes that
        // should not be searched.

        $criteria = new CDbCriteria;

        $criteria->compare('id', $this->id);
        $criteria->compare('name', $this->name, true);
        $criteria->compare('class_path', $this->class_path, true);
        $criteria->compare('subject', $this->subject, true);
        $criteria->compare('template', $this->template, true);
        $criteria->compare('status', $this->status);
        $criteria->compare('create_date', $this->create_date, true);
        $criteria->compare('update_date', $this->update_date, true);

        return new CActiveDataProvider(get_class($this), array(
        'criteria' => $criteria,
        ));
    }

}